package cn.edu.zju.om;

import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;
import java.util.regex.Pattern;

import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.AndFilter;
import org.htmlparser.filters.HasAttributeFilter;
import org.htmlparser.filters.LinkRegexFilter;
import org.htmlparser.filters.RegexFilter;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.nodes.TagNode;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;

import cn.edu.zju.om.crawler.TaskManager;
import cn.edu.zju.om.data.Comment;
import cn.edu.zju.om.data.DAO;
import cn.edu.zju.om.data.ReviewTarget;



public class DangdangProcessor implements IContentProcessor {
	
	private static final String PRODUCT_URL_PREFIX = "http://product.dangdang.com/product.aspx?product_id=";

	private static final String REVIEW_URL = "http://commu.dangdang.com/review/reviewlist.php?pid=%s&fc=0&pc=80&sort=2&page=%d";

	private static final String REVIEW_URL_START = "http://commu.dangdang.com/review/reviewlist.php?pid=";
	
	private static final String ERROR_URL = "http://commu.dangdang.com/404.php";
	
	//private static Map<String, ReviewTarget> reviewTargets = new HashMap<String, ReviewTarget>();
	
	//private static WordSegmentor segmentor = new SUWordSegmentorAdapter();
	
	//private static String lastTarget = null;
	private Queue<String> commentTaskUrls = new LinkedList<String>();
	
	private DAO<ReviewTarget, String> dao = new DAO<ReviewTarget, String>(ReviewTarget.class);
	
	@Override
	public void process(Parser parser, String parameter)
			throws IOException, ParserException {
		
		NodeList allNodes = null;
		allNodes = parser.parse(null);
		
		String url = parser.getURL();
		if (url.equals(ERROR_URL)) {
			return;
		}
		
		ReviewTarget target = updateTasks(parser, allNodes);
		
		if (target == null) {
			return;
		}
		
		while(!commentTaskUrls.isEmpty()) {
			String commentUrl = commentTaskUrls.poll();
			
			try {
				parser.reset();
				parser.setURL(commentUrl);
				
				NodeList commentPageAllNodes = null;
				commentPageAllNodes = parser.parse(null);
				
				processComments(parser, commentPageAllNodes, target);
			} catch (Exception e) {
				e.printStackTrace();
				continue;
			}
		}
		
		
		dao.makePersistent(target);

	}


	private void processComments(Parser parser, NodeList allNodes, ReviewTarget target) {
		
		String url = parser.getURL();
		if (url.equals(ERROR_URL)) {
			return;
		}
		
		NodeFilter reviewFilter = new TagNameFilter("div");
		reviewFilter = new AndFilter(reviewFilter, new HasAttributeFilter("class", "preview_content"));

		NodeList list;
		list = allNodes.extractAllNodesThatMatch(reviewFilter, true);
		

		
		if (target == null) {
			return;
		}
		
		for (int i = 0; i < list.size(); ++i) {
			Node reviewNode = list.elementAt(i);
			
			// extract author
			NodeFilter authorFilter = new LinkRegexFilter("http://commu.dangdang.com/member/[0-9]*+");
			NodeList authorList = new NodeList();
			reviewNode.collectInto(authorList, authorFilter);
			TagNode authorNode = (TagNode) authorList.elementAt(5);
			String author = authorNode.toPlainTextString();
			//System.out.println(authorNode.toPlainTextString());

			
			// extract marks
			TagNode starNode = FetchTagNode(reviewNode, "span", "class", "span_star");
			NodeFilter redStarFilter = new HasAttributeFilter("src", "../images/star_red.gif");
			NodeList redStars = new NodeList();
			reviewNode.collectInto(redStars, redStarFilter);
			int redStarCount = redStars.size();
			//System.out.println(redStarCount);
			
			
			// extract title
			TagNode titleNode = FetchTagNode(reviewNode, "div", "class", "title_time");
			String title = titleNode.toPlainTextString().trim();//toHtml();//.getNextSibling().getFirstChild().toHtml();
			title = Helper.normalize(title);
			//System.out.println(title);
			
			// extract review text
			TagNode reviewTextNode = FetchTagNode(reviewNode, "div", "class", "appear_time");
			if (reviewTextNode == null) {
				continue;
			}
			reviewTextNode = (TagNode) reviewTextNode.getNextSibling();
			String reviewText = reviewTextNode.toPlainTextString();
			reviewText = Helper.normalize(reviewText);
			
			// Segment Text
//			List<String> seggedWords;
//			try {
//				seggedWords = segmentor.segment(reviewText);
//			} catch (Exception e) {
//				e.printStackTrace();
//				continue;
//			}
//			
//			StringBuilder sb = new StringBuilder();
//			for(String word : seggedWords) {
//				sb.append(word + ' ');
//			}
//			sb.deleteCharAt(sb.length() - 1);
//			String seggedText = sb.toString();
			
			//System.out.println(author);
			
			try {
				Comment comment = new Comment(author, redStarCount, reviewText, null, title, url);
				
				target.addComment(comment);
			} catch (Exception e) {
				e.printStackTrace();
				continue;
			}

		}
		
//		DAO<ReviewTarget, String> dao = new DAO<ReviewTarget, String>(ReviewTarget.class);
//		
//		dao.makePersistent(target);
	}


	private ReviewTarget updateTasks(Parser parser, NodeList allNodes) {

		String url = parser.getURL();

		int productIDStart = url.indexOf('=') + 1;
		String productID = url.substring(productIDStart);

		if (!url.startsWith(PRODUCT_URL_PREFIX)) {
			return null;
		}

		try {
			addProductTasks(allNodes);
			// get product detailed information

			// title
			NodeFilter titleFilter = new HasAttributeFilter("class", "black000");
			NodeList titleNodeList = allNodes.extractAllNodesThatMatch(
					titleFilter, true);

			Node titleNode = titleNodeList.elementAt(0);
			String title = titleNode.toPlainTextString();

			// category
			TagNode categoryNode = FetchTagNode(allNodes.elementAt(3), "div",
					"id", "__categroy_bk");
			String category = categoryNode.toPlainTextString();
			category = category.substring(6);

			// //////////////////
			// appendix

			// author
			TagNode authorNode = FetchTagNode(allNodes.elementAt(3), "div",
					"id", "author_");
			String author = authorNode.toPlainTextString();

			// publisher
			TagNode publisherNode = FetchTagNode(allNodes.elementAt(3), "div",
					"id", "publisher_");
			String publisher = publisherNode.toPlainTextString();

			String appendix = author + "/n" + publisher;

			// EARN13 code
			NodeFilter ean13Filter = new RegexFilter("[0-9]{13}");
			NodeList ean13Nodes = allNodes.extractAllNodesThatMatch(
					ean13Filter, true);
			String barcode = null;
			for (int i = 0; i < ean13Nodes.size(); ++i) {
				Node ean13Node = ean13Nodes.elementAt(i);
				String ean13code = ean13Node.getText();
				ean13code = ean13code.replaceFirst(".*([0-9]{13}).*", "$1");
				if (Helper.isEan13Barcode(ean13code)) {
					barcode = ean13code;
					System.out.println(ean13code);
					break;
				}
			}
			if (barcode == null) {
				// No ean13 code found!
				return null;
			}

			int reviewCount = extractReviewCount(allNodes);

			int pageCount = reviewCount / 80;
			if (pageCount * 80 < reviewCount) {
				++pageCount;
			}
			for (int i = 1; i <= pageCount; ++i) {
				String taskURL = String.format(REVIEW_URL, productID, i);

				commentTaskUrls.add(taskURL);
			}

			ReviewTarget target = new ReviewTarget(title, category, barcode,
					url, appendix);
//			DAO<ReviewTarget, String> reviewDao = new DAO<ReviewTarget, String>(
//					ReviewTarget.class);
//			target = reviewDao.makePersistent(target);

			return target;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}


	private void addProductTasks(NodeList allNodes) {
		NodeFilter productsURLFilter = new LinkRegexFilter(".*product\\.aspx\\?product_id=([0-9]+).*");
		NodeList productsURLs = allNodes.extractAllNodesThatMatch(productsURLFilter, true);
		for(int i = 0; i < productsURLs.size(); ++i) {
			String urlText = ((TagNode) productsURLs.elementAt(i)).getAttribute("href");
			
			if (!Pattern.matches(".*product\\.aspx\\?product_id=([0-9]+).*", urlText)) {
				continue;
			}
			
			String productIDText = urlText.replaceAll(".*product\\.aspx\\?product_id=([0-9]+).*", "$1");
			
			String newTaskURL = PRODUCT_URL_PREFIX + productIDText;
			
			TaskManager.getInstance().addTask(newTaskURL);

		}
	}


	private int extractReviewCount(NodeList allNodes) {
		NodeFilter reviewCountFilter = new TagNameFilter("div");
		reviewCountFilter = new AndFilter(reviewCountFilter, new HasAttributeFilter("id", "div_product_reviews"));

		NodeList list;
		list = allNodes.extractAllNodesThatMatch(reviewCountFilter, true);
		
		reviewCountFilter = new TagNameFilter("em");
		list = list.extractAllNodesThatMatch(reviewCountFilter, true);
		Node reviewCountNode = list.elementAt(0);
		if (reviewCountNode != null) {
			reviewCountNode = reviewCountNode.getNextSibling();
			if (reviewCountNode != null) {
				String reviewCount = reviewCountNode.toPlainTextString();
				System.out.println(reviewCount);
				return Integer.parseInt(reviewCount);
			}
		}
		
		throw new IllegalStateException("The total review count can not be found in the page.");
	}
	
	
	private static TagNode FetchTagNode(Node parrentNode, String tagName, String attributeName, String attributeValue) {
		NodeFilter nodesFilter = new TagNameFilter(tagName);
		nodesFilter = new AndFilter(nodesFilter,
				new HasAttributeFilter(attributeName, attributeValue));
		
		NodeList nodes = new NodeList();
		parrentNode.collectInto(nodes, nodesFilter);
		
		return (TagNode) nodes.elementAt(0);
	}
	
//	private static void addReviewTarget(String pid, ReviewTarget target) {
//		reviewTargets.put(pid, target);
//	}
	
//	private static ReviewTarget getReivewTarget(String url) {
//		int start = REVIEW_URL_START.length();
//		int end = url.indexOf('&');
//		
//		if (end <= 0) {
//			return null;
//		}
//		
//		String pid = url.substring(start, end);
//		
//		// to save memory
//		if (lastTarget == null) {
//			lastTarget = pid;
//		}
//		
//		if (!lastTarget.equals(pid)) {
//			reviewTargets.remove(lastTarget);
//			lastTarget = pid;
//		}
//		
//		return reviewTargets.get(pid);
//	}

}
